{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "a15ed1a0-ca70-41cb-9771-84dab19798ee",
   "metadata": {},
   "source": [
    "# 量化交易学习"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6e71fd79-ed0c-4b9b-877a-ea9b4d7b1a73",
   "metadata": {},
   "source": [
    "## 1 Tushaer和Talib简介"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "af99b476-b878-4f2e-b376-7637ec9e3f02",
   "metadata": {},
   "source": [
    "### 1.1 tushare简介\n",
    "* 免费、开源的python财经数据接口包\n",
    "* 获取股票、基金、期货、数字货币等行情数据\n",
    "* 返回的数据格式大部分都是pandas DataFrame类型\n",
    "* 底层调用的是各大网站的数据接口，如股票使用的是sina数据接口"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6de1f130-b710-46e4-b317-42bb00e67c38",
   "metadata": {},
   "source": [
    "**tushare pro**\n",
    "* tushare升级版，有丰富的接口数据\n",
    "* 与tushare有稍微的区别\n",
    "* 部分接口使用有积分限制\n",
    "* 注册链接https://tushare.pro/register?reg=650483"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ed614f1a-a97f-46d6-b476-dc9ef9bec958",
   "metadata": {},
   "source": [
    "### 1.2 talib简介\n",
    "* Python金融量化的高级库\n",
    "* 指标丰富\n",
    "* 免费开源\n",
    "* 轻松使用"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "06e48126-e766-4dfb-ba4c-fea34a191834",
   "metadata": {},
   "source": [
    "### 1.3 环境安装\n",
    "* tushare安装\n",
    "`pip install tushare`\n",
    "* talib安装\n",
    "  * https://www.lfd.uci.edu/~gohlke/pythonlibs/\n",
    "  * pip install 下载的对应安装whl文件\n",
    "  * 或者`pip install ta-lib --index=https://pypi.vnpy.com`\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b5298b15-cc52-49d9-a51a-1e891407310a",
   "metadata": {},
   "source": [
    "## 2 简单双均线买卖策略示例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "df867c02-5c4d-4d0c-9b33-58510013cc92",
   "metadata": {},
   "outputs": [],
   "source": [
    "import tushare as ts\n",
    "import talib\n",
    "import pandas as pd\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "6ebe0549-c78c-4cd5-86b0-78e08b49918f",
   "metadata": {},
   "outputs": [],
   "source": [
    "def add_note(code):\n",
    "    '''\n",
    "    格式化股票代码00063->`00063,在股票代码前面添加符号`,防止忽略前面的0\n",
    "    param： code：股票代码\n",
    "    '''\n",
    "    return \"'\" + code"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "f51794ed-dae2-4945-8077-f9813bc3f680",
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_stock_data(code):\n",
    "    '''\n",
    "    获取数据并保存到csv文件\n",
    "    param： code：股票代码\n",
    "    return: filname: csv文件存储目录\n",
    "    '''\n",
    "    # 通过tushare获取数据，前复权后的数据\n",
    "    df_raw = ts.get_k_data(code, autype='qfg')\n",
    "    df_raw[\"code\"] = df_raw[\"code\"].apply(add_note)\n",
    "    # print(df_raw)\n",
    "    df_raw.to_csv(code + '.csv',index=False)\n",
    "    return code + '.csv'\n",
    "# help(ts.get_k_data)\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "05235946-cb60-41f5-ba4d-475b8e8d0eb1",
   "metadata": {},
   "outputs": [],
   "source": [
    "def compare_diff(param1, parame2):\n",
    "    '''\n",
    "    比较两个均线数值，如果param1>parame2，就返回1,否则就返回-1\n",
    "    '''\n",
    "    return np.where(param1>=parame2,1,-1)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "a56652cf-5bbb-49e4-9868-b5d7098a2beb",
   "metadata": {},
   "outputs": [],
   "source": [
    "def calculate_sma(filename, ma1=5, ma2=10):\n",
    "    '''\n",
    "    计算双均线\n",
    "    param: filename: 股票csv数据路径\n",
    "    param：ma1：短期均线天数\n",
    "    param：ma2: 长期均线天数\n",
    "    return：df: 包含双均线的DataFrame\n",
    "    '''\n",
    "    df = pd.read_csv(filename)\n",
    "    df['date'] = pd.to_datetime(df['date'])\n",
    "    df.set_index('date',inplace=True)\n",
    "    # Simple Moving Average SMA 简单移动均线\n",
    "    df['SMA_' + str(ma1)] = talib.MA(df['close'], timeperiod=ma1)\n",
    "    df['SMA_' + str(ma2)] = talib.MA(df['close'], timeperiod=ma2)\n",
    "    # df的数据窗口也可以求出简单移动均线\n",
    "    # df['SMA2_' + str(ma1)] = df['close'].rolling(5).mean() \n",
    "    df['compare'] = df.apply(lambda x: compare_diff(x['SMA_' + str(ma1)],x['SMA_' + str(ma2)]),axis=1)\n",
    "    df = df[9:]\n",
    "    df.to_csv('sma_data.csv')\n",
    "    return df\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "a829d272-aa02-4655-86b9-aae38b379a88",
   "metadata": {},
   "outputs": [],
   "source": [
    "def tag(match, compare):\n",
    "    '''\n",
    "    开平仓\n",
    "    param: match:DataFrame的match列\n",
    "    param: compare:DataFrame的compare列\n",
    "    '''\n",
    "    if match == False and compare == 1:\n",
    "        return '买入'\n",
    "    if match == False and compare == -1:\n",
    "        return '卖出'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "7bc26056-0c4f-4db9-93cf-f81e496bdbc2",
   "metadata": {},
   "outputs": [],
   "source": [
    "def sell_and_by(df, ma1=5, ma2=10):\n",
    "    '''\n",
    "    买入开仓，卖出平仓\n",
    "    '''\n",
    "    # 标记均线上穿和下穿\n",
    "    df['match'] = df['compare'] == df['compare'].shift(1)\n",
    "    # 开仓与平仓\n",
    "    df['tag'] = df.apply(lambda x : tag(x['match'],x['compare']),axis=1)\n",
    "    df.to_csv('result.csv')\n",
    "    return df\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "id": "176ad734-abd1-4350-a013-706b17ddbf31",
   "metadata": {},
   "outputs": [],
   "source": [
    "def cal_profit():\n",
    "    '''\n",
    "    回测数据，计算盈亏\n",
    "    '''\n",
    "    print('===简单双均线策略===')\n",
    "    df = pd.read_csv('result.csv')\n",
    "    df['date'] = pd.to_datetime(df['date'])\n",
    "    df.set_index('date',inplace=True)\n",
    "\n",
    "    df['profit'] = 0\n",
    "    total = 0\n",
    "    win = 0\n",
    "    buyprice = 0\n",
    "    \n",
    "    for index, row in df.iterrows():\n",
    "        if row.tag=='买入':\n",
    "            buyprice = row.close\n",
    "        if row.tag == '卖出' and buyprice != 0:\n",
    "            total += 1\n",
    "            # 用这种方法赋值：\n",
    "            df.loc[index,'profit'] = row.close - buyprice\n",
    "            if row.close - buyprice > 0:\n",
    "                win += 1\n",
    "                \n",
    "    percent = round((win/total)*100,2)\n",
    "    prifit = round(df['profit'].sum(),2)\n",
    "    df.to_csv('prifit.csv')\n",
    "    print('开平仓次数：',total, '\\n盈利次数：',win, '\\n成功率：', percent, '\\n盈亏：', prifit)\n",
    "    return df\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "id": "6ffb9b40-601d-4095-86dc-6d89377c335c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "本接口即将停止更新，请尽快使用Pro版接口：https://tushare.pro/document/2\n",
      "===简单双均线策略===\n",
      "开平仓次数： 39 \n",
      "盈利次数： 16 \n",
      "成功率： 41.03 \n",
      "盈亏： 1.82\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\lhl\\AppData\\Local\\Temp\\ipykernel_23368\\2788725352.py:21: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '1.5299999999999994' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.\n",
      "  df.loc[index,'profit'] = row.close - buyprice\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>open</th>\n",
       "      <th>close</th>\n",
       "      <th>high</th>\n",
       "      <th>low</th>\n",
       "      <th>volume</th>\n",
       "      <th>code</th>\n",
       "      <th>SMA_5</th>\n",
       "      <th>SMA_10</th>\n",
       "      <th>compare</th>\n",
       "      <th>match</th>\n",
       "      <th>tag</th>\n",
       "      <th>profit</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>date</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>2022-11-28</th>\n",
       "      <td>11.90</td>\n",
       "      <td>11.81</td>\n",
       "      <td>11.92</td>\n",
       "      <td>11.69</td>\n",
       "      <td>1331563.0</td>\n",
       "      <td>'000001</td>\n",
       "      <td>11.878</td>\n",
       "      <td>11.796</td>\n",
       "      <td>1</td>\n",
       "      <td>False</td>\n",
       "      <td>买入</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2022-11-29</th>\n",
       "      <td>12.16</td>\n",
       "      <td>12.99</td>\n",
       "      <td>12.99</td>\n",
       "      <td>12.13</td>\n",
       "      <td>4749277.0</td>\n",
       "      <td>'000001</td>\n",
       "      <td>12.112</td>\n",
       "      <td>11.894</td>\n",
       "      <td>1</td>\n",
       "      <td>True</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2022-11-30</th>\n",
       "      <td>12.90</td>\n",
       "      <td>13.03</td>\n",
       "      <td>13.34</td>\n",
       "      <td>12.82</td>\n",
       "      <td>3209628.0</td>\n",
       "      <td>'000001</td>\n",
       "      <td>12.354</td>\n",
       "      <td>12.015</td>\n",
       "      <td>1</td>\n",
       "      <td>True</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2022-12-01</th>\n",
       "      <td>13.38</td>\n",
       "      <td>13.10</td>\n",
       "      <td>13.66</td>\n",
       "      <td>13.07</td>\n",
       "      <td>2002689.0</td>\n",
       "      <td>'000001</td>\n",
       "      <td>12.620</td>\n",
       "      <td>12.156</td>\n",
       "      <td>1</td>\n",
       "      <td>True</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2022-12-02</th>\n",
       "      <td>13.14</td>\n",
       "      <td>12.90</td>\n",
       "      <td>13.15</td>\n",
       "      <td>12.69</td>\n",
       "      <td>1404203.0</td>\n",
       "      <td>'000001</td>\n",
       "      <td>12.766</td>\n",
       "      <td>12.287</td>\n",
       "      <td>1</td>\n",
       "      <td>True</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2025-07-01</th>\n",
       "      <td>12.06</td>\n",
       "      <td>12.30</td>\n",
       "      <td>12.33</td>\n",
       "      <td>12.06</td>\n",
       "      <td>1354341.0</td>\n",
       "      <td>'000001</td>\n",
       "      <td>12.208</td>\n",
       "      <td>12.021</td>\n",
       "      <td>1</td>\n",
       "      <td>True</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2025-07-02</th>\n",
       "      <td>12.31</td>\n",
       "      <td>12.32</td>\n",
       "      <td>12.41</td>\n",
       "      <td>12.23</td>\n",
       "      <td>1200412.0</td>\n",
       "      <td>'000001</td>\n",
       "      <td>12.260</td>\n",
       "      <td>12.076</td>\n",
       "      <td>1</td>\n",
       "      <td>True</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2025-07-03</th>\n",
       "      <td>12.33</td>\n",
       "      <td>12.35</td>\n",
       "      <td>12.42</td>\n",
       "      <td>12.28</td>\n",
       "      <td>794086.0</td>\n",
       "      <td>'000001</td>\n",
       "      <td>12.248</td>\n",
       "      <td>12.141</td>\n",
       "      <td>1</td>\n",
       "      <td>True</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2025-07-04</th>\n",
       "      <td>12.35</td>\n",
       "      <td>12.60</td>\n",
       "      <td>12.72</td>\n",
       "      <td>12.30</td>\n",
       "      <td>1775333.0</td>\n",
       "      <td>'000001</td>\n",
       "      <td>12.328</td>\n",
       "      <td>12.217</td>\n",
       "      <td>1</td>\n",
       "      <td>True</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2025-07-07</th>\n",
       "      <td>12.60</td>\n",
       "      <td>12.78</td>\n",
       "      <td>12.82</td>\n",
       "      <td>12.60</td>\n",
       "      <td>1495407.0</td>\n",
       "      <td>'000001</td>\n",
       "      <td>12.470</td>\n",
       "      <td>12.302</td>\n",
       "      <td>1</td>\n",
       "      <td>True</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>631 rows × 12 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "             open  close   high    low     volume     code   SMA_5  SMA_10  \\\n",
       "date                                                                         \n",
       "2022-11-28  11.90  11.81  11.92  11.69  1331563.0  '000001  11.878  11.796   \n",
       "2022-11-29  12.16  12.99  12.99  12.13  4749277.0  '000001  12.112  11.894   \n",
       "2022-11-30  12.90  13.03  13.34  12.82  3209628.0  '000001  12.354  12.015   \n",
       "2022-12-01  13.38  13.10  13.66  13.07  2002689.0  '000001  12.620  12.156   \n",
       "2022-12-02  13.14  12.90  13.15  12.69  1404203.0  '000001  12.766  12.287   \n",
       "...           ...    ...    ...    ...        ...      ...     ...     ...   \n",
       "2025-07-01  12.06  12.30  12.33  12.06  1354341.0  '000001  12.208  12.021   \n",
       "2025-07-02  12.31  12.32  12.41  12.23  1200412.0  '000001  12.260  12.076   \n",
       "2025-07-03  12.33  12.35  12.42  12.28   794086.0  '000001  12.248  12.141   \n",
       "2025-07-04  12.35  12.60  12.72  12.30  1775333.0  '000001  12.328  12.217   \n",
       "2025-07-07  12.60  12.78  12.82  12.60  1495407.0  '000001  12.470  12.302   \n",
       "\n",
       "            compare  match  tag  profit  \n",
       "date                                     \n",
       "2022-11-28        1  False   买入     0.0  \n",
       "2022-11-29        1   True  NaN     0.0  \n",
       "2022-11-30        1   True  NaN     0.0  \n",
       "2022-12-01        1   True  NaN     0.0  \n",
       "2022-12-02        1   True  NaN     0.0  \n",
       "...             ...    ...  ...     ...  \n",
       "2025-07-01        1   True  NaN     0.0  \n",
       "2025-07-02        1   True  NaN     0.0  \n",
       "2025-07-03        1   True  NaN     0.0  \n",
       "2025-07-04        1   True  NaN     0.0  \n",
       "2025-07-07        1   True  NaN     0.0  \n",
       "\n",
       "[631 rows x 12 columns]"
      ]
     },
     "execution_count": 79,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "if __name__ == '__main__':\n",
    "    code = '000001' # 股票代码\n",
    "    star_time = '2024-1-1'\n",
    "    end_time = '2024-12-31'\n",
    "    # 配置双均线\n",
    "    ma_5 = 5\n",
    "    ma_10 = 10\n",
    "    # 获取数据并保存到csv文件\n",
    "    filename = get_stock_data(code,star_time,endtime)\n",
    "    # 计算双均线数据并保存\n",
    "    df = calculate_sma(filename, ma_5, ma_10)\n",
    "    # 买入开仓，卖出平仓\n",
    "    sell_and_by(df, ma_5, ma_5)\n",
    "    # 计算盈利\n",
    "    df = cal_profit()\n",
    "df"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
